#!/usr/bin/python
# -*- coding: utf-8 -*-

try:
	from ConfigParser import SafeConfigParser as ConfigParser
except ImportError:
	from ConfigParser import ConfigParser

import os
CONFIG_FILE = os.path.join(os.environ['HOME'], '.wjs.ini')

import gdata.calendar.service
import gdata.calendar
import atom

from vobject import readOne as readCalendar
import urllib2, urlparse, mimetools

def login(user, password):
	calendar_service = gdata.calendar.service.CalendarService()
	calendar_service.email = user
	calendar_service.password = password
	calendar_service.source = 'xtrochu-update_shows_calendar-1.0'
	calendar_service.ProgrammaticLogin()
	return calendar_service

def GetCalendar(calendar_service, name):
	feed = calendar_service.GetOwnCalendarsFeed()
	for i, calendar in enumerate(feed.entry):
		if calendar.title.text == name:
			return calendar
	calendar = gdata.calendar.CalendarListEntry()
	calendar.title = atom.Title(text=name)
	calendar.where = gdata.calendar.Where(value_string='Paris')
	calendar.color = gdata.calendar.Color(value='#2952A3')
	calendar.timezone = gdata.calendar.Timezone(value='Europe/Paris')
	calendar.hidden = gdata.calendar.Hidden(value='false')
	new_calendar = calendar_service.InsertCalendar(new_calendar=calendar)
	return new_calendar

def GetCalendarEvents(calendar_service, calendar, filter = None):
	feed = calendar_service.GetCalendarEventFeed(calendar.GetAlternateLink().href)
	events = {}
	deletes = []
	def add_feed_events(feed):
		for i, event in enumerate(feed.entry):
			title = event.title.text
			key = filter and filter(title) or title
			if events.has_key(key):
				deletes.append(event.GetEditLink().href)
			else:
				events[key] = event
	while True:
		add_feed_events(feed)
		if feed.GetNextLink():
			feed = calendar_service.GetCalendarEventFeed(feed.GetNextLink().href)
		else:
			break
	for uri in deletes:
		calendar_service.DeleteEvent(uri)
	return events

ENCODE_TEMPLATE= """--%(boundary)s
Content-Disposition: form-data; name="%(name)s"

%(value)s
""".replace('\n','\r\n')
def encode_multipart_formdata(fields):
	"""
	fields is a sequence of (name, value) elements for regular form fields.
	Return (content_type, body) ready for httplib.HTTP instance
	"""
	BOUNDARY = mimetools.choose_boundary()
	body = ""
	for key, value in fields.iteritems():
		body += ENCODE_TEMPLATE % {
					'boundary': BOUNDARY,
					'name': str(key),
					'value': str(value)
				}
	body += '--%s--\n\r' % BOUNDARY
	content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
	return body, content_type

def LoginNextEpisode(url, login, password):
	values = {
		'username': login,
		'password': password,
	}
	body, content_type = encode_multipart_formdata(values)
	request = urllib2.Request(url, body, {'Content-Type': content_type})
	response = urllib2.urlopen(request)

def GetNextEpisodeCalendar(url):
	request = urllib2.Request(url)
	response = urllib2.urlopen(request)
	cal = readCalendar(response)
	amount = len(cal.vevent_list)
	print "got %d episodes" % amount
	assert amount <= 100
	return cal

def GetOnMyTVCalendar(url):
	request = urllib2.Request(url)
	response = urllib2.urlopen(request)
	cal = readCalendar(response)
	amount = len(cal.vevent_list)
	print "got %d episodes" % amount
	assert amount <= 100
	return cal

def GetTVShowEvents(config):
	type = config.get("calendars", "shows-use")
	if type == "next-episode":
		LoginNextEpisode(config.get("next-episode", "login-url"),
			config.get("next-episode", "login"),
			config.get("next-episode", "password"))
		return GetNextEpisodeCalendar(config.get("next-episode", "url")), None
	elif type == "on-my-tv":
		return GetOnMyTVCalendar(config.get("on-my-tv", "calendar")), lambda x: x.split(' - ', 1)[0]

def UpdateCalendarEvents(calendar_service, calendar, ne, filter = None):
	events = GetCalendarEvents(calendar_service, calendar, filter)
	print "%d existing events" % len(events.keys())
	uri = calendar.GetAlternateLink().href

	updated, added,failed_update = 0, 0, 0

	for component in ne.vevent_list:
		description = component.description.value
		summary = component.summary.value
		start = component.dtstart.value
		end = component.dtend.value

		key = filter(summary) if filter else summary

		if events.has_key(key):
			event = events[key]
			update = True
		else:
			event = gdata.calendar.CalendarEventEntry()
			update = False

		event.title = atom.Title(text=summary)
		event.content = atom.Content(text=description)
		start_time = start.strftime('%Y-%m-%dT%H:%M:%S.000Z')
		end_time = end.strftime('%Y-%m-%dT%H:%M:%S.000Z')
		while len(event.when) > 0:
			event.when.pop()
		event.when.append(gdata.calendar.When(start_time=start_time, end_time=end_time))

		if update:
			try:
				calendar_service.UpdateEvent(event.GetEditLink().href, event)
			except gdata.service.RequestError:
				failed_update += 1
			else:
				updated += 1
		else:
			calendar_service.InsertEvent(event, uri)
			added += 1
		if CLEAN and update:
			del events[key]
	if failed_update > 0:
		print "%d events updated (%d failed), %d added" % (updated, failed_update, added)
	else:
		print "%d events updated, %d added" % (updated, added)
	if CLEAN:
		cleaned = 0
		for event in events.values():
			calendar_service.DeleteEvent(event.GetEditLink().href)
			cleaned += 1
		print "%d events cleaned up" % cleaned

def main(conf = CONFIG_FILE):
	config = ConfigParser()
	config.read(conf)

	opener = urllib2.build_opener()
	opener.add_handler(urllib2.HTTPCookieProcessor())
	urllib2.install_opener(opener)

	events, filter = GetTVShowEvents(config)

	calendar_service = login(config.get("google-account", "name"), config.get("google-account", "password"))
	calendar = GetCalendar(calendar_service, config.get("calendars", "shows"))

	UpdateCalendarEvents(calendar_service, calendar, events, filter)

if __name__ == '__main__':
	import sys
	if len(sys.argv) > 1 and sys.argv[1] == 'clean':
		CLEAN = True
		print "cleaning..."
	else:
		CLEAN = False
	main()
